package com.wanghaoran.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdcardUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.wanghaoran.constant.MC;
import com.wanghaoran.dto.*;
import com.wanghaoran.entity.Assets;
import com.wanghaoran.entity.School;
import com.wanghaoran.entity.Student;
import com.wanghaoran.excel.SchoolExcel;
import com.wanghaoran.excel.StudentExcel;
import com.wanghaoran.exception.IllegalParamException;
import com.wanghaoran.exception.ServerErrorException;
import com.wanghaoran.exception.VersionException;
import com.wanghaoran.mapper.SchoolMapper;
import com.wanghaoran.mapper.StudentMapper;
import com.wanghaoran.service.SchoolService;
import com.wanghaoran.service.StudentService;
import com.wanghaoran.util.MinioUtil;
import com.wanghaoran.vo.SchoolVO;
import com.wanghaoran.vo.StudentVO;
import jakarta.annotation.Resource;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author wanghaoran
 */
@Service
@CacheConfig(cacheNames = "student")
public class StudentServiceImpl implements StudentService {

    @Resource
    private StudentMapper studentMapper;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @CacheEvict(allEntries = true)
    @Override
    public int insert(StudentInsertDTO dto) {
        String info = dto.getInfo();
        String idcard = dto.getIdcard();
        // 拷贝属性
        Student student = BeanUtil.copyProperties(dto, Student.class);

        student.setNumber("MC" + LocalDateTime.now().getYear() + generateStudentNumber());
        student.setAvatar(MC.Student.DEFAULT_AVATAR);
        student.setGender(IdcardUtil.getGenderByIdCard(idcard));
        student.setAge(IdcardUtil.getAgeByIdCard(idcard));
        student.setProvince(IdcardUtil.getProvinceByIdCard(idcard));
        student.setAddress("暂未添加详细住址");
        student.setStatus(MC.Student.STUDYING);
        student.setInfo(StrUtil.isBlank(info) ? "暂无描述" : info);
        student.setVersion(0L);
        student.setDeleted(0);
        student.setCreated(LocalDateTime.now());
        student.setUpdated(LocalDateTime.now());
        // DB添加
        int result = studentMapper.insert(student);
        if (result < 0) {
            throw new ServerErrorException("DB添加失败");
        }
        return result;
    }

    @Cacheable(key = "#p0", condition = "#p0 != null", unless = "#result == null")
    @Override
    public Student select(Long id) {
        // DB查询
        Student result = studentMapper.select(id);
        if(ObjectUtil.isNull(result)) {
            throw new ServerErrorException("ID不存在");
        }
        return result;
    }

    @Cacheable(key = "#root.methodName", unless = "#result == null")
    @Override
    public List<StudentVO> list() {
        return studentMapper.list(new StudentPageDTO())
                .stream()
                .map(student -> BeanUtil.copyProperties(student, StudentVO.class))
                .collect(Collectors.toList());
    }

    @Cacheable(key = "#root.methodName + ':' + #p0.toString()",
                condition = "#p0 != null",
                unless = "#result == null")
    @Override
    public PageInfo<Student> page(StudentPageDTO dto) {
        PageHelper.startPage(dto.getPageNum(), dto.getPageSize());
        return new PageInfo<>(studentMapper.list(dto));
    }

    @CacheEvict(allEntries = true)
    @Transactional
    @Retryable(retryFor = VersionException.class)
    @Override
    public int update(StudentUpdateDTO dto) {
        Long id = dto.getId();
        String idcard = dto.getIdcard();
        String address = dto.getAddress();
        if(ObjectUtil.isNull(id)) {
            throw new IllegalParamException("ID为空");
        }
        Student student = studentMapper.select(id);
        if(ObjectUtil.isNull(student)) {
            throw new ServerErrorException("记录不存在");
        }
        BeanUtil.copyProperties(dto, student);
        // 设置默认值
        student.setGender(IdcardUtil.getGenderByIdCard(idcard));
        student.setAge(IdcardUtil.getAgeByIdCard(idcard));
        student.setProvince(IdcardUtil.getProvinceByIdCard(idcard));
        student.setAddress(address);
        student.setUpdated(LocalDateTime.now());
        // DB修改
        int result = studentMapper.update(student);
        if (result <= 0) {
            throw new VersionException("DB修改失败");
        }
        return result;
    }

    @CacheEvict(allEntries = true)
    @Override
    public int delete(Long id) {
        int result = studentMapper.delete(id);
        if (result <= 0) {
            throw new ServerErrorException("DB删除失败");
        }
        return result;
    }

    @CacheEvict(allEntries = true)
    @Override
    public int deleteBatch(List<Long> ids) {
        int result = studentMapper.deleteBatch(ids);
        if (result <= 0) {
            throw new ServerErrorException("DB批量删除失败");
        }
        return result;
    }

    @Override
    public List<StudentExcel> getExcelData() {
        return studentMapper.list(new StudentPageDTO())
                .stream()
                .map(student -> {
                    StudentExcel studentExcel = BeanUtil.copyProperties(student, StudentExcel.class);
                    if (ObjectUtil.isNotNull(student.getSchool())) {
                        studentExcel.setSchoolTitle(student.getSchool().getTitle());
                    }
                    if (ObjectUtil.isNotNull(student.getSalesman())) {
                        studentExcel.setSalesmanName(student.getSalesman().getRealname());
                    }
                    if (ObjectUtil.isNotNull(student.getClub())) {
                        studentExcel.setClubTitle(student.getClub().getTitle());
                    }
                    studentExcel.setGender(MC.Student.genderFormat(student.getGender()));
                    studentExcel.setStatus(MC.Student.statusFormat(student.getStatus()));
                    return studentExcel;
                })
                .collect(Collectors.toList());
    }

    @Transactional(rollbackFor = RuntimeException.class)
    @CacheEvict(allEntries = true)
    @Override
    public String uploadAvatar(MultipartFile newFile, Long id) {
        // 按主键查询记录
        Student student = studentMapper.select(id);
        if (ObjectUtil.isNull(student)) {
            throw new ServerErrorException("记录不存在");
        }
        // 备份旧文件
        String oldFile = student.getAvatar();
        // 生成新文件名
        String newFilename = MinioUtil.randomFilename(newFile);
        // DB更新文件名
        student.setAvatar(newFilename);
        if (studentMapper.update(student) <= 0) {
            throw new ServerErrorException("DB更新失败");
        }

        try {
            // MinIO删除旧文件（默认文件不删除）
            if (!MC.Student.DEFAULT_AVATAR.equals(oldFile)) {
                MinioUtil.delete(oldFile, MC.MinIO.AVATAR_DIR, MC.MinIO.BUCKET_NAME);
            }
            // MinIO上传新文件
            MinioUtil.upload(newFile, newFilename, MC.MinIO.AVATAR_DIR, MC.MinIO.BUCKET_NAME);
        } catch (Exception e) {
            throw new ServerErrorException("MinIO操作失败：" + e.getMessage());
        }
        // 返回新文件名
        return newFilename;
    }

    /**
     * 生成学生编号
     * @return  学生编号
     */
    private String generateStudentNumber() {
        final String KEY = "student:globalNumber";
        // 使用Redis INCR原子自增命令（不存在时自动初始化为0并递增为1）
        Long increment = stringRedisTemplate.opsForValue().increment(KEY);
        if (increment == null) {
            throw new ServerErrorException("Redis自增操作失败");
        }
        // 自增后的值-1得到实际使用的编号（保持与原逻辑一致：首次生成00000，之后递增）
        return String.format("%05d", increment - 1);
    }


}
